#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cache.h"

/* MINIX: FORWARD _PROTOTYPE( int get_cache_size, (void)							);*/

/* MINIX: PUBLIC void init_cache(void){ */
void init_cache(void){

	sglib_hashed_CacheElement_init(CacheTable);
	system("mkdir tmp");

}

/* MINIX: PUBLIC void destroy_cache(void){ */
void destroy_cache(void){

	struct sglib_hashed_CacheElement_iterator it;
	CacheElement *le;
	
	for(le=sglib_hashed_CacheElement_it_init(&it,CacheTable); le!=NULL; le=sglib_hashed_CacheElement_it_next(&it)) {
		free(le);
	}
}

/* MINIX: PRIVATE int get_cache_stats(CacheStats *cache_st){ */
void get_cache_stats(CacheStats *cache_st){
	
	int i=0;
	
	CacheElement *le;
	struct sglib_hashed_CacheElement_iterator ListIt;
	
	/* TODO: Within below loops fill in cache_st with detailed	*
	 * info about what cache consists of in a final state		*/
	for(le = sglib_hashed_CacheElement_it_init(&ListIt,CacheTable); le!=NULL && i<10; le = sglib_hashed_CacheElement_it_next(&ListIt),i++)
		/* TODO: Find an efficient way to print the # of cell ([i])*/
		printf("%s [MISS_ST]:%d\n",le->key,le->miss_st);

	/* Get total number of entries in cache */
	cache_st->cache_sz = 0;
	for(i=0;i<HASH_TAB_SIZE;i++){
		cache_st->cache_sz += sglib_CacheElement_len(CacheTable[i]);
	}
}

char *construct_delete_cmd(const char *key){
	
	char *delete_cmd, *rep_name;
	
	rep_name = malloc(NAME_MAX * sizeof(char));
	delete_cmd = malloc(CMD_SIZE * sizeof(char));
	
	strncpy(rep_name,REPLICAS_FOLDER,sizeof(REPLICAS_FOLDER));
	strcat(rep_name,key);
	strcat((char*) rep_name,REPLICA_EXT);
	strncpy(delete_cmd,DELETE_CMD,sizeof(DELETE_CMD));
	strcat(delete_cmd,SPACE);
	strcat(delete_cmd,rep_name);
	return delete_cmd;
}

/* MINIX: PRIVATE void create_replica(const char *key){ */
void create_replica(const char *key){

	int repdes;
	char *rep_name;
	
	rep_name = malloc(NAME_MAX*sizeof(char ));
	strncpy(rep_name,REPLICAS_FOLDER,sizeof(REPLICAS_FOLDER));
	strcat(rep_name,key);
	strcat((char*) rep_name,REPLICA_EXT);
	printf("Creating Replica for key: [%s]\n",key);
	
	if ((repdes = open(rep_name, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU)) < 0) {
		printf("Error occured while opening file\n");
		exit(-1);
	}
	memset(rep_name,0,sizeof(rep_name));
}

/* MINIX: PRIVATE void delete_replica(char *delete_cmd)*/
void delete_replica(char *delete_cmd){
	system(delete_cmd);
}

/* MINIX: PUBLIC void insert_to_cache(const char *key,CacheElement *ne){ */
void insert_to_cache(const char *key){
	
	struct sglib_hashed_CacheElement_iterator it;
	CacheElement *le,*ne;
	CacheStats cache_st;
	char *delete_cmd;
	
	ne = malloc(sizeof(CacheElement));
	strncpy(ne->key,key,strlen(key));
	ne->hits = 0;
	
	/* Increase most recent hit status to assume *
	 * least recently used element				 */
	get_cache_stats(&cache_st);
	if(cache_st.cache_sz>=HASH_TAB_SIZE){
		for(le = sglib_hashed_CacheElement_it_init(&it,CacheTable); le!=NULL; le = sglib_hashed_CacheElement_it_next(&it)){
			/* FIXME: This is not correct.
				Find eldest entry in cache */
			if(le->miss_st >= HASH_TAB_SIZE - 1){
				/* Delete eldest entry */
				sglib_hashed_CacheElement_delete(CacheTable, le);
				printf("Deleting replica for key :%s\n",le->key);
				delete_cmd = construct_delete_cmd(le->key);
				printf("Command: %s\n", delete_cmd);
				delete_replica(delete_cmd);
				break;
			}
		}
	}
	sglib_hashed_CacheElement_add(CacheTable, ne);
	create_replica(key);
	
	printf("{Just Added}:%s\n",ne->key);
}

/* MINIX: PUBLIC void update_cache_stats(CacheElement *ne){*/
void update_cache_stats(const char *key){

	struct sglib_hashed_CacheElement_iterator it;
	CacheElement *le;
	/* Update stats */
	for(le = sglib_hashed_CacheElement_it_init(&it,CacheTable); le!=NULL; le = sglib_hashed_CacheElement_it_next(&it))
		if(strcmp(le->key,key)) (le->miss_st)++;
		else le->miss_st=0; /* FIXME: This has to be updated with find_member and then deleting */
}

/* MINIX: PUBLIC CacheElement * belongs_to_cache(const char *key){ */
int belongs_to_cache(const char *key){

	int res;
	CacheElement *ce,*ne;
	
	ce = malloc(sizeof(CacheElement *));
	strncpy(ce->key,key,strlen(key));
	
	if((ne = sglib_hashed_CacheElement_find_member(CacheTable, ce))!=NULL){
		res=FOUND;
	} else{
		res=NOT_FOUND;
	}
	return res;
}

/* MINIX: PRIVATE char * construct_command(const char *key) */
char * construct_diff_cmd(const char *key){
	
	char *diff_cmd, *rep_name;
	/* FIXME: Find a way not to repeat code (see	*
	 * create_replica when creating rep_name)		*/
	rep_name = malloc(100*sizeof(char ));
	diff_cmd = malloc(CMD_SIZE*sizeof(char));
	
	strncpy(rep_name,(char*)key,sizeof(key));
	strcat((char*) rep_name,REPLICA_EXT);
	
	strncpy(diff_cmd,UNIFIED_DIFF_CMD, sizeof(UNIFIED_DIFF_CMD));
	
	/* FIXME: When integrating we have to fix an extension
	 * issue here. If DFS receives oldfile.txt we have to
	 * extract the "txt" extension from the filename and 
	 * attach "tmp". */
	strcat(diff_cmd,key);
	strcat(diff_cmd,SPACE);
	strcat(diff_cmd,rep_name);
	strcat(diff_cmd,SPACE);
	strcat(diff_cmd,APPEND);
	strcat(diff_cmd,SPACE);
	strcat(diff_cmd,DIFF_FILE);
	
	return diff_cmd;
}

/* MINIX: PRIVATE char * construct_patch_command(){ 			*/
char *construct_patch_cmd(void){
	char *patch_cmd;
	patch_cmd = malloc(CMD_SIZE*sizeof(char));
	strncpy(patch_cmd,PATCH_CMD, sizeof(PATCH_CMD));
	strcat(patch_cmd,SPACE);
	strcat(patch_cmd,"<");
	strcat(patch_cmd,SPACE);
	strcat(patch_cmd,DIFF_FILE);
	return patch_cmd;
}

/* MINIX: PRIVATE void apply_patch(char *diff_cmd, char *patch_cmd){*/
void apply_patch(char *diff_cmd, char *patch_cmd){
	system(diff_cmd);
	system(patch_cmd);
}

int main(int argc, char **argv){

	int i, res, total_sz, cache_sz;

	CacheElement ce, *ne, *le;
	CacheStats cache_st;

	struct sglib_hashed_CacheElement_iterator it;
	struct sglib_CacheElement_iterator ListIt;

	init_cache();
	
	for(i=1; i<argc; i++){
		res = belongs_to_cache(argv[i]);
		if( res == NOT_FOUND ){/* Element not found in cache */
			insert_to_cache(argv[i]);
		}/* Elenement already exists in cache */
		update_cache_stats(argv[i]);
	}
	get_cache_stats(&cache_st);
	destroy_cache();
	return OK;
}
